// 普通的拖拽
interface I_Drag1EventData {
    target: HTMLElement;
    currentTarget: EventTarget;
    start?: {
        evt: PointerEvent,
        targetOffsetX: number,
        targetOffsetY: number,
        pointerPageX: number,
        pointerPageY: number
    };
    move?: {
        evt: PointerEvent,
        pointerDiffX: number,
        pointerDiffY: number,
        targetOffsetX: number,
        targetOffsetY: number
    };
    end?: {
        evt: PointerEvent
    };
}

interface I_Drag1Opts {
    diffSize?: number, // 偏移量超过此值开始拖动
    draggingClassName?: string, // 拖拽中的样式
    restrictParentContainer?: boolean, // 限制在父容器范围内移动
    handle?: string, // 拖拽dom
    xAxis?: boolean, // 允许水平拖拽
    yAxis?: boolean, // 允许垂直拖拽
    onDragStart?: (evt: PointerEvent, data: I_Drag1EventData) => void,
    onDragMove?: (evt: PointerEvent, data: I_Drag1EventData) => void | boolean, // 方法return false时，不执行交互，只返回拖拽数据
    onDragEnd?: (evt: PointerEvent, data: I_Drag1EventData) => void,
}

interface I_Drag1Result {
    enable: () => void,
    disable: () => void,
    destroy: () => void,
    setOpts: (opts: I_Drag1Opts) => void
}

/** 传统拖拽 */
export default function drag(el: string | HTMLElement, opts: I_Drag1Opts): I_Drag1Result | undefined {

    let target: HTMLElement = typeof el === 'string' ? document.querySelector(el) as HTMLElement : el;

    if (!(target instanceof HTMLElement)) {
        return;
    }

    let enabled = true;

    opts = {
        diffSize: 0, // 偏移量超过此值开始拖动
        draggingClassName: 'dragging', // 拖拽中的样式
        restrictParentContainer: true, // 限制在父容器范围内移动
        handle: '', // 拖拽dom
        xAxis: true, // 允许水平拖拽
        yAxis: true, // 允许垂直拖拽
        onDragStart(_evt: PointerEvent, _data: I_Drag1EventData) { },
        onDragMove(_evt: PointerEvent, _data: I_Drag1EventData) { }, // 方法return false时，不执行交互，只返回拖拽数据
        onDragEnd(_evt: PointerEvent, _data: I_Drag1EventData) { },
        ...opts,
    };

    let pointerdown = (evtDown: PointerEvent): void => {

        let currentTarget = evtDown.target as EventTarget;

        if (!enabled || (opts.handle && target.querySelector(opts.handle) !== currentTarget)) {
            return;
        }

        evtDown.preventDefault();

        // 拖拽回调返回的数据
        let data: I_Drag1EventData = {
            target,
            currentTarget,
            start: {
                evt: evtDown,
                targetOffsetX: target.offsetLeft,
                targetOffsetY: target.offsetTop,
                pointerPageX: evtDown.pageX,
                pointerPageY: evtDown.pageY
            }
        };

        let isMove = false;
        let pointermove = (evtMove: PointerEvent): void => {

            let pointerDiffX: number = evtMove.pageX - evtDown.pageX;
            let pointerDiffY: number = evtMove.pageY - evtDown.pageY;
            let targetOffsetX: number = data.start!.targetOffsetX;
            let targetOffsetY: number = data.start!.targetOffsetY;

            if (opts.xAxis) {

                targetOffsetX = data.start!.targetOffsetX + pointerDiffX;

                if (opts.restrictParentContainer) {
                    if (targetOffsetX <= 0) {
                        targetOffsetX = 0;
                    } else if (targetOffsetX > (target.parentNode as HTMLElement).clientWidth - target.clientWidth) {
                        targetOffsetX = (target.parentNode as HTMLElement).clientWidth - target.clientWidth;
                    }
                }
            }

            if (opts.yAxis) {
                targetOffsetY = data.start!.targetOffsetY + pointerDiffY;

                if (opts.restrictParentContainer) {
                    if (targetOffsetY <= 0) {
                        targetOffsetY = 0;
                    } else if (targetOffsetY > (target.parentNode as HTMLElement).clientHeight - target.clientHeight) {
                        targetOffsetY = (target.parentNode as HTMLElement).clientHeight - target.clientHeight;
                    }
                }
            }

            data.move = {
                evt: evtMove,
                pointerDiffX,
                pointerDiffY,
                targetOffsetX,
                targetOffsetY,
            };

            // 首次移动
            if (!isMove && (!opts.diffSize || Math.abs(pointerDiffX) + Math.abs(pointerDiffY)) > opts.diffSize!) {
                target.classList.add(opts.draggingClassName!);
                opts.onDragStart!(evtMove, data);
                isMove = true;
            }

            // 移动
            if (isMove && opts.onDragMove!(evtMove, data) !== false) {
                target.style.left = data.move.targetOffsetX + 'px';
                target.style.top = data.move.targetOffsetY + 'px';
            }
        };

        let pointerup = (evtUp: PointerEvent): void => {

            if (isMove) {
                isMove = false;

                data.end = {
                    evt: evtUp,
                };

                target.classList.remove(opts.draggingClassName!);
                opts.onDragEnd!(evtUp, data);
            }

            window.removeEventListener('pointermove', pointermove);
            window.removeEventListener('pointerup', pointerup);
        };

        window.addEventListener('pointermove', pointermove);
        window.addEventListener('pointerup', pointerup);
    };

    target.addEventListener('pointerdown', pointerdown);

    return {
        enable() {
            enabled = true;
        },

        disable() {
            enabled = false;
        },

        destroy() {
            target.removeEventListener('pointerdown', pointerdown);
        },

        setOpts(newOpts: I_Drag1Opts) {
            Object.assign(opts, newOpts);
        }
    };
}
